/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 2002,2003-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sun.org.apache.xerces.internal.impl.xs;

import com.sun.org.apache.xerces.internal.impl.xs.util.XSObjectListImpl;
import com.sun.org.apache.xerces.internal.xs.XSAnnotation;
import com.sun.org.apache.xerces.internal.xs.XSConstants;
import com.sun.org.apache.xerces.internal.xs.XSModelGroup;
import com.sun.org.apache.xerces.internal.xs.XSNamespaceItem;
import com.sun.org.apache.xerces.internal.xs.XSObjectList;

/**
 * Store schema model group declaration.
 *
 * @author Sandy Gao, IBM
 * @version $Id: XSModelGroupImpl.java,v 1.7 2010-11-01 04:39:55 joehw Exp $
 * @xerces.internal
 */
public class XSModelGroupImpl implements XSModelGroup {

  // types of model groups
  // REVISIT: can't use same constants as those for particles, because
  // there are place where the constants are used together. For example,
  // to check whether the content is an element or a sequence.
  public static final short MODELGROUP_CHOICE = 101;
  public static final short MODELGROUP_SEQUENCE = 102;
  public static final short MODELGROUP_ALL = 103;

  // compositor of the model group
  public short fCompositor;

  // particles
  public XSParticleDecl[] fParticles = null;
  public int fParticleCount = 0;

  // this particle's optional annotations
  public XSObjectList fAnnotations = null;

  // whether this model group contains nothing
  public boolean isEmpty() {
    for (int i = 0; i < fParticleCount; i++) {
      if (!fParticles[i].isEmpty()) {
        return false;
      }
    }
    return true;
  }

  /**
   * 3.8.6 Effective Total Range (all and sequence) and
   * Effective Total Range (choice)
   * The following methods are used to return min/max range for a particle.
   * They are not exactly the same as it's described in the spec, but all the
   * values from the spec are retrievable by these methods.
   */
  public int minEffectiveTotalRange() {
    if (fCompositor == MODELGROUP_CHOICE) {
      return minEffectiveTotalRangeChoice();
    } else {
      return minEffectiveTotalRangeAllSeq();
    }
  }

  // return the sum of all min values of the particles
  private int minEffectiveTotalRangeAllSeq() {
    int total = 0;
    for (int i = 0; i < fParticleCount; i++) {
      total += fParticles[i].minEffectiveTotalRange();
    }
    return total;
  }

  // return the min of all min values of the particles
  private int minEffectiveTotalRangeChoice() {
    int min = 0, one;
    if (fParticleCount > 0) {
      min = fParticles[0].minEffectiveTotalRange();
    }

    for (int i = 1; i < fParticleCount; i++) {
      one = fParticles[i].minEffectiveTotalRange();
      if (one < min) {
        min = one;
      }
    }

    return min;
  }

  public int maxEffectiveTotalRange() {
    if (fCompositor == MODELGROUP_CHOICE) {
      return maxEffectiveTotalRangeChoice();
    } else {
      return maxEffectiveTotalRangeAllSeq();
    }
  }

  // if one of the max value of the particles is unbounded, return unbounded;
  // otherwise return the sum of all max values
  private int maxEffectiveTotalRangeAllSeq() {
    int total = 0, one;
    for (int i = 0; i < fParticleCount; i++) {
      one = fParticles[i].maxEffectiveTotalRange();
      if (one == SchemaSymbols.OCCURRENCE_UNBOUNDED) {
        return SchemaSymbols.OCCURRENCE_UNBOUNDED;
      }
      total += one;
    }
    return total;
  }

  // if one of the max value of the particles is unbounded, return unbounded;
  // otherwise return the max of all max values
  private int maxEffectiveTotalRangeChoice() {
    int max = 0, one;
    if (fParticleCount > 0) {
      max = fParticles[0].maxEffectiveTotalRange();
      if (max == SchemaSymbols.OCCURRENCE_UNBOUNDED) {
        return SchemaSymbols.OCCURRENCE_UNBOUNDED;
      }
    }

    for (int i = 1; i < fParticleCount; i++) {
      one = fParticles[i].maxEffectiveTotalRange();
      if (one == SchemaSymbols.OCCURRENCE_UNBOUNDED) {
        return SchemaSymbols.OCCURRENCE_UNBOUNDED;
      }
      if (one > max) {
        max = one;
      }
    }
    return max;
  }

  /**
   * get the string description of this particle
   */
  private String fDescription = null;

  public String toString() {
    // REVISIT: Commented code may help to eliminate redundant parentheses (test first before committing)
    if (fDescription == null) {
      StringBuffer buffer = new StringBuffer();
      if (fCompositor == MODELGROUP_ALL) {
        buffer.append("all(");
      } else  //if (fMinOccurs != 1 || fMaxOccurs != 1)
      {
        buffer.append('(');
      }
      if (fParticleCount > 0) {
        buffer.append(fParticles[0].toString());
      }
      for (int i = 1; i < fParticleCount; i++) {
        if (fCompositor == MODELGROUP_CHOICE) {
          buffer.append('|');
        } else {
          buffer.append(',');
        }
        buffer.append(fParticles[i].toString());
      }
      //if (fCompositor == MODELGROUP_ALL || fMinOccurs != 1 || fMaxOccurs != 1)
      buffer.append(')');
      fDescription = buffer.toString();
    }
    return fDescription;
  }

  public void reset() {
    fCompositor = MODELGROUP_SEQUENCE;
    fParticles = null;
    fParticleCount = 0;
    fDescription = null;
    fAnnotations = null;
  }

  /**
   * Get the type of the object, i.e ELEMENT_DECLARATION.
   */
  public short getType() {
    return XSConstants.MODEL_GROUP;
  }

  /**
   * The <code>name</code> of this <code>XSObject</code> depending on the
   * <code>XSObject</code> type.
   */
  public String getName() {
    return null;
  }

  /**
   * The namespace URI of this node, or <code>null</code> if it is
   * unspecified.  defines how a namespace URI is attached to schema
   * components.
   */
  public String getNamespace() {
    return null;
  }

  /**
   * {compositor} One of all, choice or sequence. The valid constants values
   * are: ALL, CHOICE, SEQUENCE.
   */
  public short getCompositor() {
    if (fCompositor == MODELGROUP_CHOICE) {
      return XSModelGroup.COMPOSITOR_CHOICE;
    } else if (fCompositor == MODELGROUP_SEQUENCE) {
      return XSModelGroup.COMPOSITOR_SEQUENCE;
    } else {
      return XSModelGroup.COMPOSITOR_ALL;
    }
  }

  /**
   * {particles} A list of particles
   */
  public XSObjectList getParticles() {
    return new XSObjectListImpl(fParticles, fParticleCount);
  }

  /**
   * Optional. Annotation.
   */
  public XSAnnotation getAnnotation() {
    return (fAnnotations != null) ? (XSAnnotation) fAnnotations.item(0) : null;
  }

  /**
   * Optional. Annotations.
   */
  public XSObjectList getAnnotations() {
    return (fAnnotations != null) ? fAnnotations : XSObjectListImpl.EMPTY_LIST;
  }

  /**
   * @see org.apache.xerces.xs.XSObject#getNamespaceItem()
   */
  public XSNamespaceItem getNamespaceItem() {
    return null;
  }

} // class XSModelGroupImpl
